/*-------------------------------------------------------------------------
 *
 * utils.c
 *
 *    Utility module of Postgres-XC configuration and operation tool.
 *
 * Copyright (c) 2013 Postgres-XC Development Group
 *
 *-------------------------------------------------------------------------
 */
/*
 * Variable useful tools/small routines.
 */
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <unistd.h>
#include <errno.h>
#include <stdio.h>
#include <time.h>
#include <stdio.h>

#include "../../src/interfaces/libpq/libpq-fe.h"
#include "utils.h"
#include "pgxc_ctl.h"
#include "pgxc_ctl_log.h"
#include "do_shell.h"
#include "config.h"
#include "variables.h"
#include "varnames.h"

static int Malloc_ed = 0;
static int Strdup_ed = 0;
static int Freed = 0;

void* Malloc(size_t size)
{
    void* rv = malloc(size);

    Malloc_ed++;
    if (rv == NULL) {
        elog(PANIC, "PANIC: No more memory.  See core file for details.\n");
        abort();
    }
    return (rv);
}

char** addToList(char** List, char* val)
{
    char** rv;
    int ii;

    for (ii = 0; List[ii]; ii++)
        ;
    rv = Realloc(List, sizeof(char*) * ii);
    rv[ii - 1] = NULL;
    return rv;
}

void* Malloc0(size_t size)
{
    void* rv = malloc(size);

    Malloc_ed++;
    if (rv == NULL) {
        elog(PANIC, "PANIC: No more memory.  See core file for details.\n");
        abort();
    }
    memset(rv, 0, size);
    return (rv);
}

void* Realloc(void* ptr, size_t size)
{
    void* rv = realloc(ptr, size);

    if (rv == NULL) {
        elog(PANIC, "PANIC: No more memory.  See core file for details.\n");
        abort();
    }
    return (rv);
}

void Free(void* ptr)
{
    Freed++;
    if (ptr)
        free(ptr);
}

/*
 * If flag is TRUE and chdir fails, then exit(1)
 */
int Chdir(char* path, int flag)
{
    if (chdir(path)) {
        elog(ERROR,
            "ERROR: Could not change work directory to \"%s\". %s%s\n",
            path,
            flag == TRUE ? "Exiting. " : "",
            strerror(errno));
        if (flag == TRUE)
            exit(1);
        else
            return -1;
    }
    return 0;
}

FILE* Fopen(char* path, char* mode)
{
    FILE* rv = NULL;

    if ((rv = fopen(path, mode)) == NULL)
        elog(ERROR, "ERROR: Could not open the file \"%s\" in \"%s\", %s\n", path, mode, strerror(errno));
    return (rv);
}

char* Strdup(const char* s)
{
    char* rv = NULL;

    Strdup_ed++;
    rv = strdup(s);
    if (rv == NULL) {
        elog(PANIC, "PANIC: No more memory. See core file for details.\n");
        abort();
    }
    return (rv);
}

void appendFiles(FILE* f, char** fileList)
{
    FILE* src = NULL;
    int ii;
    char buf[MAXLINE + 1];

    if (fileList)
        for (ii = 0; fileList[ii]; ii++) {
            if (!is_none(fileList[ii])) {
                if ((src = fopen(fileList[ii], "r")) == 0) {
                    elog(ERROR, "ERROR: could not open file %s for read, %s\n", fileList[ii], strerror(errno));
                    continue;
                }
                while (fgets(buf, MAXLINE, src))
                    fputs(buf, f);
                fclose(src);
            }
        }
}

FILE* prepareLocalStdin(char* buf, int len, char** fileList)
{
    FILE* f = NULL;
    if ((f = fopen(createLocalFileName(STDIN, buf, len), "w")) == NULL) {
        elog(ERROR, "ERROR: could not open file %s for write, %s\n", buf, strerror(errno));
        return (NULL);
    }
    appendFiles(f, fileList);
    return (f);
}

char* timeStampString(char* buf, int len)
{
    time_t nowTime;
    struct tm nowTm;

    nowTime = time(NULL);
    localtime_r(&nowTime, &nowTm);

    snprintf(buf,
        len,
        "%04d%02d%02d_%02d:%02d:%02d",
        nowTm.tm_year + 1900,
        nowTm.tm_mon + 1,
        nowTm.tm_mday,
        nowTm.tm_hour,
        nowTm.tm_min,
        nowTm.tm_sec);
    return (buf);
}

char** makeActualNodeList(char** nodeList)
{
    char** actualNodeList;
    int ii, jj;

    for (ii = 0, jj = 0; nodeList[ii]; ii++) {
        if (!is_none(nodeList[ii]))
            jj++;
    }
    actualNodeList = Malloc0(sizeof(char*) * (jj + 1));
    for (ii = 0, jj = 0; nodeList[ii]; ii++) {
        if (!is_none(nodeList[ii])) {
            actualNodeList[jj] = Strdup(nodeList[ii]);
            jj++;
        }
    }
    return actualNodeList;
}

int gtmProxyIdx(char* gtmProxyName)
{
    int ii;

    for (ii = 0; aval(VAR_gtmProxyNames)[ii]; ii++) {
        if (strcmp(aval(VAR_gtmProxyNames)[ii], gtmProxyName) == 0)
            return ii;
    }
    return -1;
}

int coordIdx(char* coordName)
{
    int ii;

    if (is_none(coordName))
        return -1;
    for (ii = 0; aval(VAR_coordNames)[ii]; ii++) {
        if (strcmp(aval(VAR_coordNames)[ii], coordName) == 0)
            return ii;
    }
    return -1;
}

int datanodeIdx(char* datanodeName)
{
    int ii;

    if (is_none(datanodeName))
        return -1;
    for (ii = 0; aval(VAR_datanodeNames)[ii]; ii++) {
        if (strcmp(aval(VAR_datanodeNames)[ii], datanodeName) == 0)
            return ii;
    }
    return -1;
}

int getEffectiveGtmProxyIdxFromServerName(char* serverName)
{
    int ii;

    if (serverName == NULL)
        return (-1);
    for (ii = 0; aval(VAR_gtmProxyNames)[ii]; ii++) {
        if (strcmp(aval(VAR_gtmProxyServers)[ii], serverName) == 0)
            return ii;
    }
    return -1;
}

/*
 * Please note that this function deeply depend upon
 * the environment.
 *
 * It works find with CentOS/Ubuntu/ReadHat Linux but
 * may need another tweak for other operation systems
 * such as Solaris, FreeBSD, MacOS.
 */
pid_t get_prog_pid(char* host, char* progname, char* dir)
{
    char cmd[MAXLINE + 1];
    char pid_s[MAXLINE + 1];
    int ii;
    FILE* wkf = NULL;
    char* token = NULL;
    char* line = NULL;

    snprintf(cmd,
        MAXLINE,
        "ssh %s@%s "
        "\"ps -f -C %s | grep %s\"",
        sval(VAR_pgxcUser),
        host,
        progname,
        dir);
    wkf = popen(cmd, "r");
    if (wkf == NULL) {
        elog(ERROR,
            "ERROR: cannot obtain pid value of the remote postmaster, host \"%s\" dir \"%s\", %s\n",
            host,
            dir,
            strerror(errno));
        return (-1);
    }
    fgets(pid_s, MAXLINE, wkf);
    fclose(wkf);
    /* Get the second token */
    line = pid_s;
    if ((line = get_word(line, &token)) == NULL)
        return 0;
    get_word(line, &token);
    if (token == NULL)
        return 0;
    for (ii = 0; token[ii]; ii++)
        if (token[ii] < '0' || token[ii] > '9')
            return 0;
    return (atoi(token));
}

int pingNode(char* host, char* port)
{
    PGPing status;
    char conninfo[MAXLINE + 1];
    char editBuf[MAXPATH + 1];

    conninfo[0] = 0;
    if (host) {
        snprintf(editBuf, MAXPATH, "host = '%s' ", host);
        strncat(conninfo, editBuf, MAXLINE);
    }
    if (port) {
        snprintf(editBuf, MAXPATH, "port = %d ", atoi(port));
        strncat(conninfo, editBuf, MAXLINE);
    }
    if (conninfo[0]) {
        status = PQping(conninfo);
        if (status == PQPING_OK)
            return 0;
        else
            return 1;
    } else
        return -1;
}

void trimNl(char* s)
{
    for (; *s && *s != '\n'; s++)
        ;
    *s = 0;
}

char* getChPidList(char* host, pid_t ppid)
{
    FILE* wkf = NULL;
    char cmd[MAXLINE + 1];
    char line[MAXLINE + 1];
    char* rv = Malloc(MAXLINE + 1);

    rv[0] = 0;
    snprintf(cmd, MAXLINE, "ssh %s@%s pgrep -P %d", sval(VAR_pgxcUser), host, ppid);
    wkf = popen(cmd, "r");
    if (wkf == NULL)
        return NULL;
    while (fgets(line, MAXLINE, wkf)) {
        trimNl(line);
        strncat(rv, line, MAXLINE);
        strncat(rv, " ", MAXLINE);
    }
    return rv;
}

char* getIpAddress(char* hostName)
{
    char command[MAXLINE + 1];
    char* ipAddr = NULL;
    FILE* f = NULL;

    snprintf(command, MAXLINE, "ping -c1 %s | head -n 1 | sed 's/^[^(]*(\\([^)]*\\).*$/\\1/'", hostName);
    if ((f = popen(command, "r")) == NULL) {
        elog(ERROR, "ERROR: could not open the command, \"%s\", %s\n", command, strerror(errno));
        return NULL;
    }
    ipAddr = Malloc(MAXTOKEN + 1);
    fgets(ipAddr, MAXTOKEN, f);
    fclose(f);
    trimNl(ipAddr);
    return ipAddr;
}
